package com.fuyun.rpc.client.loadbalancing;

import com.fuyun.rpc.common.domain.ServiceMetaInfo;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 一致性 Hash 算法实现
 *
 * @author: zrq1351
 * @date: 2025/2/16
 */
public class ConsistentHashLoadBalancer implements LoadBalancer {

	/**
	 * 虚拟节点数
	 */
	private static final int VIRTUAL_NODE_NUM = 100;
	/**
	 * 一致性 Hash 环，存放虚拟节点
	 */
	private final TreeMap<Integer, ServiceMetaInfo> virtualNodes = new TreeMap<>();

	/**
	 * 根据请求参数和服务元信息列表选择最合适的服务 该方法使用一致性哈希算法来选择服务，以实现负载均衡
	 *
	 * @param requestParams       请求参数，包含调用服务的请求信息
	 * @param serviceMetaInfoList 服务元信息列表，包含所有可选服务的详细信息
	 * @return 返回选中的服务元信息，如果没有合适的服 务则返回null
	 */
	@Override
	public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
		if (serviceMetaInfoList.isEmpty()) {
			return null;
		}

		// 构建虚拟节点环
		for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
			for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
				int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i);
				virtualNodes.put(hash, serviceMetaInfo);
			}
		}

		// 获取调用请求的 hash 值
		int hash = getHash(requestParams);

		// 选择最接近且大于等于调用请求 hash 值的虚拟节点
		Map.Entry<Integer, ServiceMetaInfo> entry = virtualNodes.ceilingEntry(hash);
		if (entry == null) {
			// 如果没有大于等于调用请求 hash 值的虚拟节点，则返回环首部的节点
			entry = virtualNodes.firstEntry();
		}
		return entry.getValue();
	}

	/**
	 * Hash 算法，可自行实现
	 * 该方法用于计算给定对象的哈希值
	 * 哈希算法的选择对于哈希表的性能至关重要，一个好的哈希算法应该能够均匀分布键值，以减少碰撞
	 * 此处的实现直接使用了对象的hashCode方法，这是一个基本且通用的做法
	 * 但是，对于特定的使用场景，可能需要实现更复杂的哈希算法，以优化性能或满足特定需求
	 *
	 * @param key 要计算哈希值的对象
	 * @return 对象的哈希值
	 */
	private int getHash(Object key) {
		return key.hashCode();
	}
}